/*
 * Copyright (C) 2019
 * <tanghaifeng-gz@loongson.cn> <pengren.mcu@qq.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 *
 */
#include <common.h>
#include <dm.h>
#include <clk.h>
#include <clk-uclass.h>
#include <asm/io.h>

#include <mach/ls2h.h>

DECLARE_GLOBAL_DATA_PTR;

static void calc_clocks(void)
{
	u32 bootcfg, refclk; //参考时钟固定为100MHz
	u32 ctrl = 0;
	unsigned int pll_odf;
	unsigned int mult, div;
	int i;

	bootcfg = readl(LS2H_CHIP_SAMPLE0) >> 16;
	if (bootcfg & 0x1) {
		refclk = 100000000 / 1000;	//使用内部100MHz备份参考时钟 (硬件模式下)
	} else {
		refclk = OSC_CLK / 1000;	//使用外部25MHz晶振
	}

	//判断cpu pll是否使用纯硬件配置
	if (bootcfg & (0x1 << 6)) {
		gd->cpu_clk = refclk * ((((bootcfg >> 4) & 0x3) << 1) + 4) * 1000;	//硬件模式下cpu pll倍频配置
	} else {
		ctrl = (u32)readl(LS2H_CLOCK_CTRL0);
		mult = (ctrl >> 1) & 0x7f;
		pll_odf = (ctrl >> 8) & 0x7;
		div = 2;
		if (pll_odf) {
			for (i=1; i<pll_odf; i++) {
				div *= 2;
			}
		} else {
			div = 1;
		}
		gd->cpu_clk = (refclk * mult / div) * 1000;
	}

	//判断ddr pll是否使用纯硬件配置
	if (bootcfg & (0x1 << 9)) {
		//硬件模式下cpu pll倍频配置
		int mult = (bootcfg >> 7) & 0x3;
		u32 rate = 0;
		switch(mult) {
		case 0:
			rate = refclk * 5;
			break;
		case 1:
			rate = refclk * 8;
			break;
		case 2:
			rate = refclk * 10;
			break;
		}
		gd->mem_clk = rate / 3 * 1000;
	} else {
		gd->mem_clk = 266666666;
	}

	//判断sys pll是否使用纯硬件配置
	if (bootcfg & (0x1 << 10)) {
		gd->bus_clk = 3000000000 / 24;
	} else {
		ctrl = (u32)readl(LS2H_CLOCK_CTRL2);
		mult = (ctrl >> 1) & 0x7f;
		pll_odf = (ctrl >> 8) & 0x7;
		div = 2;
		if (pll_odf) {
			for (i=1; i<pll_odf; i++) {
				div *= 2;
			}
		} else {
			div = 1;
		}
		gd->bus_clk = (refclk * mult / div / 24) * 1000;
	}
}

/* arch specific CPU init after DM */
int arch_cpu_init_dm(void)
{
	int ret;
	struct udevice *dev;

	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
	if (ret) {
		printf("clk-uclass not found\n");
		return 0;
	}

	return 0;
}

int mach_cpu_init(void)
{
	calc_clocks();

	return 0;
}

int dram_init(void)
{
#if 0
	gd->ram_size = get_ram_size ((long *)CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_SIZE);
#else
	if (fdtdec_setup_mem_size_base() != 0)
		return -EINVAL;
#endif
	return 0;
}

int dram_init_banksize(void)
{
	fdtdec_setup_memory_banksize();

	return 0;
}

void board_add_ram_info(int use_default)
{
	puts("(can be use by u-boot)");
	putc('\n');
	puts("DRAM Total: ");
	print_size(CONFIG_SYS_SDRAM_SIZE, "");
}

const char *get_core_name(void)
{
	u32 proc_id;
	const char *str;

	proc_id = read_c0_prid() & 0xffff;
	switch (proc_id) {
	case 0x00006305:
		str = "LS2H";
		break;
	default:
		str = "Unknown";
	}

	return str;
}

int print_cpuinfo(void)
{
	printf("Core: %s\n", get_core_name());
	printf("Speed: Cpu @ %ld MHz/ Mem @ %ld MHz/ Bus @ %ld MHz\n",
			gd->cpu_clk/1000000, gd->mem_clk/1000000, gd->bus_clk/1000000);
	return 0;
}
